# Author: slowchen
# Time: 2019/6/5 10:11
# Description: V2版本微信机器人


import re
import sys
from datetime import datetime, timedelta

import itchat
import pymysql
from apscheduler.schedulers.background import BackgroundScheduler
from itchat.content import *
from loguru import logger as log

log.add('ssr.log')


class ReplyText:
    EXPIRED_MSG = '[温馨提示] 截至到%s，您购买的SSR已过期\n[SSR信息] %s:%s\n[SSR链接] %s\n[金额] %s\n[上次付款时间] %s\n[过期时间] %s\n[购买用户] %s\n如需二维码，请在微信中搜索 草料二维码小程序 自行生成。\n如需继续使用，请[续费]'
    NO_EXPIRED = '[温馨提示] 截至到今天，暂无到期的用户'
    NO_USER_BY_REMARK_NAME = '[系统] 未发现备注为%s的用户'
    SSR_INFO = '[SSR信息] %s:%s\n[SSR链接] %s\n[金额] %s\n[上次付款时间] %s\n[过期时间] %s\n[购买用户] %s\n如需二维码，请在微信中搜索 草料二维码小程序 自行生成。'
    RECEIVE_RED_ENVELOPE = '收到红包，请在手机上查看'
    NO_RED_ENVELOPE = '[温馨提示] 请使用转账，联系我退还此红包。'
    USE_TRANSFER = '[温馨提示] 请转账%s元，注意，是转账不是红包，并且金额不能多也不能少，否则无效。'
    AMOUNT_ERROR = '[温馨提示] 金额不正确，请重新转账，并联系我退还此次转账'
    RENEWALS_SUCCESS = '[温馨提示] 续费成功'
    NOT_FIND_USER = '[系统] 未找到用户'
    DELETE_SUCCESS = '[系统] 删除端口为%s的用户成功'
    UPDATE_SUCCESS = '[系统] 更新端口%s上次付款时间为%s完成'


class Command:
    OPTION = '功能'
    EXPIRED = '过期'
    INQUIRE = '查询'
    DELETE = '删除'
    UPDATE = '更新'
    NOTICE = '通知'
    RENEWALS = '续费'
    ALL_USER = '全部'
    ADMIN_NAME = '机器人遥控器'


class Sql:
    def __init__(self, database):
        self.conn = pymysql.connect(
            user='root',
            password='cj123456',
            host='127.0.0.1',
            port=3306,
            database=database,
            use_unicode=True,
            charset="utf8"
        )


# 获取日期
def get_day(days=0):
    """
    获取N天前或者后的时间，默认取当天的日期
    :param days: 根据当前日期，获取将来或过去的日期,正数为将来的日期，负数为过去的日期
    :return: 日期
    """
    now = datetime.now()
    date = now + timedelta(days=days)
    date = date.strftime('%Y-%m-%d')
    return date


# 获取用户列表
def query_user(q_type, host=None):
    """
    查询用户列表
    :param q_type: EXPIRED查询过期的用户；ALL查询所有用户；HOST查询某个域名下的用户
    :param host:域名，默认为空，当查询类型为HOST时不能为空
    :return:
    """
    sql = Sql('ssr')
    conn = sql.conn
    cursor = conn.cursor()
    log.debug('%s连接数据库' % sys._getframe().f_code.co_name)
    if q_type == 'EXPIRED':
        log.debug('查询过期用户')
        cursor.execute(
            "select host,port,price,last_buy_date,customer_type,customer,ssr_link from `order` where last_buy_date<='%s' AND status=0" % get_day(
                days=-30))
    elif q_type == 'ALL':
        log.debug('查询全部用户')
        cursor.execute(
            'select host,port,price,last_buy_date,customer_type,customer,ssr_link from `order` where status = 0')
    elif q_type == 'HOST' and host is not None:
        log.debug('查询%s用户' % host)
        cursor.execute(
            "select host,port,price,last_buy_date,customer_type,customer,ssr_link from `order` where status = 0 and host = '%s'" % host)
    else:
        cursor.execute(
            "select host,port,price,last_buy_date,customer_type,customer,ssr_link from `order` where port = '0000'")
    users = cursor.fetchall()
    return users


# 接收关键字 “功能” 后的操作
def option():
    msg = '请输入关键字指令进行操作，例如：过期、查询、删除、更新、通知、全部，格式如下：\n' \
          '过期：发送关键字 [过期]\n' \
          '查询：发送关键字 [查询+端口号]\n' \
          '更新：发送关键字 [更新+端口号+日期]\n' \
          '删除：发送关键字 [删除+端口号]\n' \
          '通知：发送关键字 [通知+服务器+通知内容]\n' \
          '全部：发送关键字 [全部]'
    admin.send(msg)
    log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))


# 接收关键字 “过期” 后的操作
def inquire_expired():
    expired_list = query_user(q_type='EXPIRED')
    if len(expired_list) > 0:
        for expired_user in expired_list:
            host, port, price, last_buy_date, customer_type, customer, ssr_link = expired_user
            expired_day = (last_buy_date + timedelta(days=30)).strftime('%Y-%m-%d')
            msg = ReplyText.EXPIRED_MSG % (get_day(), host, port, ssr_link, price, last_buy_date, expired_day, customer)
            remark_name = 'vpn%s@%s' % (port, price)
            try:
                user = itchat.search_friends(remarkName=remark_name)[0]
                user.send(msg)
                log.debug('%s =====>>> %s' % (msg, remark_name))
            except IndexError:
                admin.send(ReplyText.NO_USER_BY_REMARK_NAME % remark_name)
                log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))
    else:
        msg = ReplyText.NO_EXPIRED
        admin.send(msg)
        log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))


# 接收关键字 “过期” 后的操作
def scheduler_expired():
    log.debug('定时任务开始执行')
    expired_list = query_user(q_type='EXPIRED')
    if len(expired_list) > 0:
        for expired_user in expired_list:
            host, port, price, last_buy_date, customer_type, customer, ssr_link = expired_user
            expired_day = (last_buy_date + timedelta(days=30)).strftime('%Y-%m-%d')
            msg = ReplyText.EXPIRED_MSG % (get_day(), host, port, ssr_link, price, last_buy_date, expired_day, customer)
            remark_name = 'vpn%s@%s' % (port, price)
            try:
                user = itchat.search_friends(remarkName=remark_name)[0]
                user.send(msg)
                log.debug('%s =====>>> %s' % (msg, remark_name))
            except IndexError:
                admin.send(ReplyText.NO_USER_BY_REMARK_NAME % remark_name)
                log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))
    else:
        msg = ReplyText.NO_EXPIRED
        admin.send(msg)
        log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))
    log.debug('定时任务执行结束')


# 接收关键字 “查询” 后的操作
def inquire_one(port, from_user):
    sql = Sql('ssr')
    conn = sql.conn
    cursor = conn.cursor()
    cursor.execute(
        "select host,port,price,last_buy_date,customer_type,customer,ssr_link from `order` where status = 0 and port='%s'" % port)
    user = cursor.fetchone()
    if user:
        host, port, price, last_buy_date, customer_type, customer, ssr_link = user
        expired_day = (last_buy_date + timedelta(days=30)).strftime('%Y-%m-%d')
        msg = ReplyText.SSR_INFO % (host, port, ssr_link, price, last_buy_date, expired_day, customer)
        if from_user == admin:
            admin.send(msg)
            log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))
        else:
            from_user.send(msg)
            log.debug('%s =====>>> %s' % (msg, from_user['RemarkName']))
    else:
        msg = ReplyText.NOT_FIND_USER
        admin.send(msg)
        log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))


# 接收关键字 “全部” 后的操作
def inquire_all():
    all_users = query_user(q_type='ALL')
    for user in all_users:
        host, port, price, last_buy_date, customer_type, customer, ssr_link = user
        expired_day = (last_buy_date + timedelta(days=30)).strftime('%Y-%m-%d')
        msg = ReplyText.SSR_INFO % (host, port, ssr_link, price, last_buy_date, expired_day, customer)
        admin.send(msg)
        log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))


# 接收关键字 “删除” 后的操作
def delete_user(port):
    sql = Sql('ssr')
    conn = sql.conn
    cursor = conn.cursor()
    cursor.execute("update `order` set status = 1 where port = '%s'" % port)
    conn.commit()
    msg = ReplyText.DELETE_SUCCESS % port
    admin.send(msg)
    log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))


# 接收关键字 “更新” 后的操作
def update_time(port, time):
    sql = Sql('ssr')
    conn = sql.conn
    cursor = conn.cursor()
    cursor.execute("update `order` set last_buy_date = '%s' where port = '%s'" % (time, port))
    conn.commit()
    msg = ReplyText.UPDATE_SUCCESS % (port, time)
    admin.send(msg)
    log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))


# 接收关键字 “通知” 后的操作
def notice_msg(msg, host=None):
    if host:
        user_list = query_user(q_type='HOST', host=host)
    else:
        user_list = query_user(q_type='ALL')
    if user_list:
        for user in user_list:
            host, port, price, last_buy_date, customer_type, customer, ssr_link = user
            to_user = itchat.search_friends(remarkName='vpn%s@%s' % (port, price))[0]
            to_user.send(msg)
            log.debug('%s =====>>> %s' % (msg, to_user['RemarkName']))
            log.debug('成功发送%d人' % len(user_list))
    else:
        admin.send(msg)
        log.debug('%s =====>>> %s' % (msg, Command.ADMIN_NAME))
    log.debug('发布通知 %s' % msg)


# 接收关键字 “续费“ 后的操作
def renewals(from_user):
    from_user_name = from_user['RemarkName']
    from_user.send(ReplyText.USE_TRANSFER % (from_user_name.split('@')[1]))
    log.debug('%s =====>>> %s' % (ReplyText.USE_TRANSFER % (from_user_name.split('@')[1]), from_user_name))


def receive_red_envelope(from_user):
    from_user.send(ReplyText.NO_RED_ENVELOPE)
    log.debug('%s =====>>> %s' % (ReplyText.NO_RED_ENVELOPE, from_user['RemarkName']))


def amount_error(from_user):
    from_user.send(ReplyText.AMOUNT_ERROR)
    log.debug('%s =====>>> %s' % (ReplyText.AMOUNT_ERROR, from_user['RemarkName']))


def renewals_success(from_user):
    from_user.send(ReplyText.RENEWALS_SUCCESS)
    log.debug('%s =====>>> %s' % (ReplyText.RENEWALS_SUCCESS, from_user['RemarkName']))


# 接收关键字并回应
@itchat.msg_register([TEXT, NOTE], isFriendChat=True)
def reply_msg(msg):
    from_user_name = msg['User']['RemarkName']
    from_user = itchat.search_friends(remarkName=from_user_name)[0]
    print(from_user)
    print(from_user_name)
    if from_user_name == Command.ADMIN_NAME:
        info = msg["Content"]
        log.debug('管理员输入指令：%s' % info)
        # 取输入信息的前两个字，然后对比预设的关键字，如果匹配则执行对应的操作
        key = info[:2]
        if key == Command.OPTION:
            option()
        elif key == Command.INQUIRE:
            port = info[2:]
            inquire_one(port, from_user)
        elif key == Command.EXPIRED:
            inquire_expired()
        elif key == Command.ALL_USER:
            inquire_all()
        elif key == Command.DELETE:
            port = info[2:]
            delete_user(port)
        elif key == Command.UPDATE:
            port = info[2:6]
            time = info[-10:]
            update_time(port, time)
        elif key == Command.NOTICE:
            result = re.findall(
                r"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b", info)
            if result:
                host = result[0]
            else:
                host = None
            notice = info.split('@')[1]
            notice_msg(notice, host)
        else:
            admin.send('无效指令')
            log.debug('无效指令')
            option()
    elif 'vpn' in from_user_name and '@' in from_user_name:
        content = msg["Content"]
        text = msg['Text']
        if content == ReplyText.RECEIVE_RED_ENVELOPE:
            log.debug('[%s] %s' % (from_user_name, ReplyText.RECEIVE_RED_ENVELOPE))
            receive_red_envelope(from_user)
        elif content == Command.RENEWALS:
            log.debug('[%s] %s' % (from_user_name, Command.RENEWALS))
            renewals(from_user)
        elif '收到转账' in text:
            user_price = float('%.2f' % float(text[4:8]))
            log.debug('用户%s实际转账%s' % (from_user_name, str(user_price)))
            sql = Sql('ssr')
            conn = sql.conn
            cursor = conn.cursor()
            cursor.execute(
                "select price,last_buy_date from `order` where status = 0 and port='%s'" % from_user_name[3:7])
            data = cursor.fetchone()
            if data:
                price, last_buy_date = data
                log.debug('用户%s应转账%d' % (from_user_name, price))
                expired_date = last_buy_date + timedelta(days=30)
                if user_price == price:
                    # 仅在点击确认转账时发送消息，收到转账时不发送
                    to_user_name = msg['ToUserName']
                    wx_name = from_user['UserName']
                    if to_user_name == wx_name:
                        update_time(from_user_name[3:7], expired_date)
                        renewals_success(from_user)
                        inquire_one(from_user_name[3:7], from_user)
                else:
                    amount_error(from_user)
                    log.info('[%s] %s' % (from_user_name, ReplyText.AMOUNT_ERROR))
    else:
        log.debug('%s为无效用户' % from_user_name)


if __name__ == '__main__':
    itchat.auto_login(hotReload=True, enableCmdQR=2)
    admin = itchat.search_friends(remarkName=Command.ADMIN_NAME)[0]
    scheduler = BackgroundScheduler()
    scheduler.add_job(scheduler_expired, 'cron', hour=11, minute=30)
    scheduler.start()
    itchat.run()
